package com.base.baseweb;

import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.Data;
import lombok.Setter;
import org.springframework.stereotype.Component;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;

public abstract class PaddingFetchPostProcessorFactory<C> implements FetchPostProcessorFactory<C>{

    @Component
    public static class DatePaddingFetchPostProcessorFactory extends PaddingFetchPostProcessorFactory<DatePaddingFetchPostProcessorFactory.Config> {
        public DatePaddingFetchPostProcessorFactory.Config resolve(Map<String, Object> config){
            ObjectMapper mapper = new ObjectMapper();
            mapper.registerModule(new JavaTimeModule());
            return mapper.convertValue(config, DatePaddingFetchPostProcessorFactory.Config.class);
        }

        public FetchPostProcessor apply(Config config){
            return new FetchPostProcessor() {
                @Override
                public void process(List<JSONObject> in, VariableHolder<List<JSONObject>> out, FetchPostProcessorChain chain) {
                    // 获取任意一个刻度
                    LocalDateTime anchor = LocalDateTime.parse(in.get(0).getString(config.field));

                    // 根据range和单位创建刻度尺
                    List<LocalDateTime> scale = createScale(anchor, config.range[0], config.range[1], config.unit, config.step);

                    // 使用二分查找定位in数据中最接近的刻度
                    JSONObject[] arr = new JSONObject[scale.size()];
                    for (JSONObject obj : in) {
                        LocalDateTime dateTime = (LocalDateTime) obj.get(config.field);
                        int index = Collections.binarySearch(scale, dateTime,  (a, b) -> a.isBefore(b) ? -1 : a.isAfter(b) ? 1 : 0);
                        if (index < 0) {
                            index = -index - 1;
                        }
                        arr[index] = obj;
                    }

                    // 将索引下为null的刻度设置为模板数据
                    JSONObject template = template(in.get(0));
                    for (int i = 0; i < arr.length; i++) {
                        if (arr[i] == null){
                            arr[i] = template;
                        }
                    }

                    // 写入到out
                    out.value = new ArrayList<>(Arrays.asList(arr));

                    chain.doPostProcess(out.value ,out);
                }
                private List<LocalDateTime> createScale(LocalDateTime anchor, LocalDateTime s, LocalDateTime e, TimeUnit unit, int step) {
                    // 刻度尺
                    List<LocalDateTime> scale = new ArrayList<>();

                    // 获取时间戳
                    long anchor_tt = getTime(anchor);
                    long range_s_tt = getTime(s);

                    long start_offset = (anchor_tt - range_s_tt) /  unit.toMillis(step);
                    LocalDateTime start = anchor.plus(-start_offset * unit.toMillis(step),  ChronoUnit.MILLIS);
                    while (start.isBefore(e) || start.isEqual(e)) {
                        scale.add(start);
                        start = start.plus(unit.toMillis(step), ChronoUnit.MILLIS);
                    }
                    return scale;
                }

                private long getTime(LocalDateTime dt){
                    // 将 LocalDateTime 转换为 Instant
                    Instant instant = dt.atZone(ZoneId.systemDefault()).toInstant();
                    // 获取时间戳（以毫秒为单位）
                    return instant.toEpochMilli();
                }

                private JSONObject template(JSONObject fact){
                    JSONObject temp = new JSONObject();
                    for (Map.Entry<String, Object> entry : fact.entrySet()) {
                        if (entry.getValue() instanceof Number) {
                            temp.put(entry.getKey(), 0);
                        }else {
                            temp .put(entry.getKey(), entry.getValue());
                        }
                    }
                    return temp;
                }
            };
        }


        @Setter
        @Data
        public static class Config{
            private String field;
            @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
            private LocalDateTime[] range;
            private TimeUnit unit;
            private int step;
            private boolean useDataAsScale = true;
        }
    }
}
